<template>
  <div class="particle-con" ref="particleCon">
    <particle-item v-for="(item, index) in lists" :key="index" :item="item" :ref="setItemRef"></particle-item>
  </div>
</template>

<script>
import { defineComponent } from 'vue'
import ParticleItem from '@/components/dynamic/particle-item.vue'
import { EventBus } from '@/store/index'
import { reactive } from 'vue'
import { MOBILE_WID_HEI } from '@/common/const-dif'

export default defineComponent({
  name: 'ParticleCon',
  data() {
    return {
      itemRefs: [],
      lists: [],
    }
  }, 
  setup() {
    const state = reactive({});
    const state_ = {
      isStopTick: false,
      timeStamp: 0,
      particle: null,
      _pool: [],
      frameTime: 0,
      numParticles: 0,
      particles: [],
    }
    _.merge(state, state_)
    return state
  },
  components: {
    ParticleItem
  },
  created() {
    //{"particle":{"width":87,"count":18,"all":40,"jsonUrl":"https://static.hd.xxx.com/upload/biz/1/leaftexiao.json"}}
    EventBus.$on('particleData', ({particle, url, config}) => {
      for(let i = 0; i < particle.all; i++) {
        this.lists.push({
          url,
        })
      }
      this.particle = particle;
      this.parseConfig(config);
      this.init();
    });
  },
  mounted() {
    this.init();
  },
  methods: {
    setItemRef(el) {
      if (el) {
        this.itemRefs.push(el);
        if(this.itemRefs.length >= this.particle.all && this._pool.length === 0) {
          this.init();
        }
      }
    },
    init() {
      if(!this.particle || this.itemRefs.length <= 0) return
      let i = 0;
      for(i; i < this.particle.count; i++) {
        this._pool.push(this.itemRefs[i]);
        this.addOneParticle();
      }
      for(i; i < this.particle.all; i++) {
        this._pool.push(this.itemRefs[i]);
      }
      this.start();
    },
    update(timeStamp) {
      let dt = (timeStamp - this.timeStamp);
      this.timeStamp = timeStamp;
      if (this.isStopTick) {
        return;
      }
      //粒子数很少的时候可能会错过添加粒子的时机
      if (this.emissionTime == -1 || this.emissionTime > 0) {
        this.frameTime += dt;
        while (this.frameTime > 0) {
          if (this.numParticles < this.maxParticles) {//需要添加粒子
            this.addOneParticle();
          }
          this.frameTime -= this.emissionRate;
        }
        if (this.emissionTime != -1) {
          this.emissionTime -= dt;
          if (this.emissionTime < 0) {
            this.emissionTime = 0;
          }
        }
      }

      var particle;
      var particleIndex = 0;
      while (particleIndex < this.numParticles) {
        particle = this.particles[particleIndex];
        if (particle.currentTime < particle.totalTime) {
          this.advanceParticle(particle, dt);
          particle.update();
          particle.currentTime += dt;
          particleIndex++;
        }
        else {
          this.removeParticle(particle);
          particle.update();
        }
      }
      // this.$renderDirty = true;
      if (this.numParticles == 0 && this.emissionTime == 0) {
        // egret.stopTick(this.update, this);
        this.onStopTick();
        // this.dispatchEventWith(egret.Event.COMPLETE);
      }
      // _.forEach(this.itemRefs, (particle) => {
      //   this.advanceParticle(particle, dt);
      //   particle.update();
      // })
      window.requestAnimationFrame(this.update);
    },
    parseConfig(config) {
      this.emitterX = getValue(config.emitter.x);
      this.emitterY = getValue(config.emitter.y);

      this.emitterXVariance = getValue(config.emitterVariance.x);
      this.emitterYVariance = getValue(config.emitterVariance.y);

      this.gravityX = getValue(config.gravity.x);
      this.gravityY = getValue(config.gravity.y);

      this.maxParticles = getValue(config.maxParticles);

      this.speed = getValue(config.speed);
      this.speedVariance = getValue(config.speedVariance);

      this.lifespan = Math.max(0.01, getValue(config.lifespan));
      this.lifespanVariance = getValue(config.lifespanVariance);

      this.emitAngle = getValue(config.emitAngle);
      this.emitAngleVariance = getValue(config.emitAngleVariance);

      this.startSize = getValue(config.startSize);
      this.startSizeVariance = getValue(config.startSizeVariance);
      this.endSize = getValue(config.endSize);
      this.endSizeVariance = getValue(config.endSizeVariance);

      this.startRotation = getValue(config.startRotation);
      this.startRotationVariance = getValue(config.startRotationVariance);
      this.endRotation = getValue(config.endRotation);
      this.endRotationVariance = getValue(config.endRotationVariance);

      this.radialAcceleration = getValue(config.radialAcceleration);
      this.radialAccelerationVariance = getValue(config.radialAccelerationVariance);
      this.tangentialAcceleration = getValue(config.tangentialAcceleration);
      this.tangentialAccelerationVariance = getValue(config.tangentialAccelerationVariance);

      this.startAlpha = getValue(config.startAlpha);
      this.startAlphaVariance = getValue(config.startAlphaVariance);
      this.endAlpha = getValue(config.endAlpha);
      this.endAlphaVariance = getValue(config.endAlphaVariance);
      
      this.particleBlendMode = config.blendMode;
      this.emissionRate = this.lifespan / this.maxParticles;


      function getValue(value) {
        if (typeof value == "undefined") {
          return 0;
        }
        return value;
      }
    },
    initParticle(particle) {
      let locParticle = particle;
      locParticle.x = this.emitterX;
      locParticle.y = this.emitterY;
      locParticle.currentTime = 0;
      locParticle.totalTime = 1000;

      let lifespan = this.getValue(this.lifespan, this.lifespanVariance);

      locParticle.currentTime = 0;
      locParticle.totalTime = lifespan > 0 ? lifespan : 0;

      if (lifespan <= 0) {
          return;
      }

      locParticle.x = this.getValue(this.emitterX, this.emitterXVariance);
      locParticle.y = this.getValue(this.emitterY, this.emitterYVariance);
      locParticle.startX = this.emitterX;
      locParticle.startY = this.emitterY;

      let angle = this.getValue(this.emitAngle, this.emitAngleVariance);
      let speed = this.getValue(this.speed, this.speedVariance);
      locParticle.velocityX = speed * Math.cos(angle);
      locParticle.velocityY = speed * Math.sin(angle);

      locParticle.radialAcceleration = this.getValue(this.radialAcceleration, this.radialAccelerationVariance);
      locParticle.tangentialAcceleration = this.getValue(this.tangentialAcceleration, this.tangentialAccelerationVariance);

      let startSize = this.getValue(this.startSize, this.startSizeVariance);
      if (startSize < 0.1) {
          startSize = 0.1;
      }
      let endSize = this.getValue(this.endSize, this.endSizeVariance);
      if (endSize < 0.1) {
          endSize = 0.1;
      }
      let textureWidth = this.particle.width/MOBILE_WID_HEI.adaptiveScale;
      locParticle.scale = startSize / textureWidth;
      locParticle.scaleDelta = ((endSize - startSize) / lifespan) / textureWidth;

      let startRotation = this.getValue(this.startRotation, this.startRotationVariance);
      let endRotation = this.getValue(this.endRotation, this.endRotationVariance);
      locParticle.rotation = startRotation;
      locParticle.rotationDelta = (endRotation - startRotation) / lifespan;

      let startAlpha = this.getValue(this.startAlpha, this.startAlphaVariance);
      let endAlpha = this.getValue(this.endAlpha, this.endAlphaVariance);

      locParticle.alpha = startAlpha;
      locParticle.alphaDelta = (endAlpha - startAlpha) / lifespan;

      locParticle.blendMode = this.particleBlendMode;
    },
    advanceParticle(particle, dt) {
      let locParticle = particle;
      dt = dt / 1000;

      let restTime = locParticle.totalTime - locParticle.currentTime;
      dt = restTime > dt ? dt : restTime;
      locParticle.currentTime += dt;

      let distanceX = locParticle.x - locParticle.startX;
      let distanceY = locParticle.y - locParticle.startY;
      let distanceScalar = Math.sqrt(distanceX * distanceX + distanceY * distanceY);
      if (distanceScalar < 0.01) {
          distanceScalar = 0.01;
      }

      let radialX = distanceX / distanceScalar;
      let radialY = distanceY / distanceScalar;
      let tangentialX = radialX;
      let tangentialY = radialY;

      radialX *= locParticle.radialAcceleration;
      radialY *= locParticle.radialAcceleration;

      let temp = tangentialX;
      tangentialX = -tangentialY * locParticle.tangentialAcceleration;
      tangentialY = temp * locParticle.tangentialAcceleration;

      locParticle.velocityX += dt * (this.gravityX + radialX + tangentialX);
      locParticle.velocityY += dt * (this.gravityY + radialY + tangentialY);
      locParticle.x += locParticle.velocityX * dt;
      locParticle.y += locParticle.velocityY * dt;

      locParticle.scale += locParticle.scaleDelta * dt * 1000;
      if (locParticle.scale < 0) {
          locParticle.scale = 0;
      }
      locParticle.rotation += locParticle.rotationDelta * dt * 1000;
      locParticle.alpha += locParticle.alphaDelta * dt * 1000;
    },
    getValue(base, variance) {
      return base + variance * (Math.random() * 2 - 1);
    },
    onStopTick() {
      this.isStopTick = true;
    },
    removeParticle(particle) {
      var index = this.particles.indexOf(particle);
      if (index != -1) {
        particle.reset();
        this.particles.splice(index, 1);
        this._pool.push(particle);
        this.numParticles--;
        // if (this.bitmapNodeList.length > this.numParticles) {
        //   this.bitmapNodeList.length = this.numParticles;
        //   this.$renderNode.drawData.length = this.numParticles;
        // }
        return true;
      }
      else {
        return false;
      }
    },
    clear() {
      while (this.particles.length) {
        this.removeParticle(this.particles[0]);
      }
      this.numParticles = 0;
      // this.$renderNode.drawData.length = 0;
      // this.bitmapNodeList.length = 0;
      // this.$renderDirty = true;
      this._pool = [];
    },
    start(duration = -1) {
      if (this.emissionRate != 0) {
        this.emissionTime = duration;
        window.requestAnimationFrame(this.update);
      }
    },
    addOneParticle() {
      var particle = this._pool.pop();
      if(particle) {
        this.initParticle(particle);
        if (particle.totalTime > 0) {
          this.particles.push(particle);
          this.numParticles++;
        }
      }
    },
  },
  beforeUpdate() {
    this.itemRefs = []
  },
})

</script>

<style>

</style>